#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <sys/stat.h>
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>
#include "bundle.h"

namespace lcl_backup
{
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    public:
        FileUtil(const std::string &filename)
            : _filename(filename)
        {
        }
        bool Remove()
        {
            if (this->Exists() == false)
            {
                return true;
            }
            // remove标志库中的一个调用
            remove(_filename.c_str());
            return true;
        }
        // 获取文件大小
        int64_t FileSize()
        {
            struct stat st;
            // int stat(const char *path, struct stat *buf);
            // 这里的path是一个指向以null结尾的字符串的指针，表示需要查询信息的文件或目录的路径。
            // 比如：查询lcl/cjr/lcl.txt；path可以是相对路径lcl.txt，前提是当前的工作目录是 /lcl/cjr/
            // 也可以是绝对路径：lcl/cjr/lcl.txt
            int n = stat(_filename.c_str(), &st);
            if (n < 0)
            {
                std::cout << "Get file size failed!" << std::endl;
                return -1;
            }
            return st.st_size;
        }
        // 文件最后一次访问时间
        time_t LastATime()
        {
            struct stat st;
            int n = stat(_filename.c_str(), &st);
            if (n < 0)
            {
                std::cout << "Get file LastATime failed!" << std::endl;
                return -1;
            }
            return st.st_atime;
        }
        // 文件最后一次修改时间
        time_t LastMTime()
        {
            struct stat st;
            int n = stat(_filename.c_str(), &st);
            if (n < 0)
            {
                std::cout << "Get file LastMTime failed!" << std::endl;
                return -1;
            }
            return st.st_mtime;
        }
        // 获取文件名 /abc/c/lcl.txt -> lcl.txt
        std::string FileName()
        {
            size_t pos = _filename.find_last_of("/");
            if (pos == std::string::npos)
            {
                return _filename;
            }
            return _filename.substr(pos + 1);
        }
        // 获取文件指定位置，指定长度的数据
        bool GetPosLen(std::string *body, size_t pos, size_t len)
        {
            if (pos + len > this->FileSize())
            {
                std::cout << "get file len is error!" << std::endl;
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename, std::ios::binary);
            if (ifs.is_open() == false)
            {
                std::cout << "read open file failed!" << std::endl;
                return false;
            }
            ifs.seekg(pos, std::ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0], len);
            if (ifs.good() == false)
            {
                std::cout << "get file content failed!" << std::endl;
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        // 从文件获取全部数据
        bool GetContent(std::string *body)
        {
            size_t size = this->FileSize();
            return GetPosLen(body, 0, size);
        }
        // 向文件写入数据，将body中的数据写入到_filename中
        bool SetContent(const std::string &body)
        {
            std::ofstream ofs;
            ofs.open(_filename, std::ios::binary);
            if (ofs.is_open() == false)
            {
                std::cout << "write open file failed!" << std::endl;
                return false;
            }
            ofs.write(&body[0], body.size());
            if (ofs.good() == false)
            {
                std::cout << "write file content failed!" << std::endl;
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        }
        // 压缩文件，将当前文件的数据压缩到packname中
        bool Compress(const std::string &packname)
        {
            // 1. 获取源文件数据
            std::string body;
            if (this->GetContent(&body) == false)
            {
                std::cout << "compress get file content failed!" << std::endl;
                return false;
            }
            // 2. 对数据压缩
            std::string packed = bundle::pack(bundle::LZIP, body);
            // 3. 将压缩的数据存到压缩包packname中
            FileUtil newfile(packname);
            if (newfile.SetContent(packed) == false) // 向newfile文件中写入压缩后的数据packed
            {
                std::cout << "compress write packed data failed!" << std::endl;
                return false;
            }
            return true;
        }
        // 解压缩文件，这里的传入的filename是解压缩后数据写入位置
        bool UnCompress(const std::string &filename)
        {
            // 1. 讲当前压缩包的数据读取出来
            std::string body;
            if (this->GetContent(&body) == false)
            {
                std::cout << "uncompress get file content failed!" << std::endl;
                return false;
            }
            // 2. 对压缩的数据解压缩
            std::string unpacked = bundle::unpack(body);
            // 3. 将解压缩后的数据写入到新文件
            FileUtil newfile(filename);
            newfile.SetContent(body);
            return true;
        }
        // 判断文件是否存在
        bool Exists()
        {
            return fs::exists(_filename);
        }
        // 创建文件目录
        bool CreateDirectory()
        {
            if (this->Exists())
                return true;
            return fs::create_directories(_filename);
        }
        // 获取指定文件夹中所有文件路径名称
        bool ScanDirectory(std::vector<std::string> *arry)
        {
            for (auto &p : fs::directory_iterator(_filename))
            {
                if (fs::is_directory(p) == true)
                    continue;
                // relative_path 带有路径的文件名
                arry->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }

    private:
        std::string _filename;
    };

    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root, std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            // write 成功返回0
            if (sw->write(root, &ss) != 0)
            {
                std::cout << "json write failed!" << std::endl;
                return false;
            }
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const std::string &str, Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);
            if (ret == false)
            {
                std::cout << "parse error: " << err << std::endl;
                return false;
            }
            return true;
        }
    };
}